#include "DijkstraApp.h"

#include <string>
#include <cstdio>


DijkstraApp::DijkstraApp()
{
	m_mode = 0;
	m_source = Vertex::Null;
	m_target = Vertex::Null;
	m_graphFileName = L"";
	m_layoutFileName = L"";
}


DijkstraApp::~DijkstraApp()
{
}


HRESULT DijkstraApp::OnCreate()
{
	return Win32Application::OnCreate();
}


void DijkstraApp::OnRelease()
{
}


bool DijkstraApp::OnMessage(UINT msg, WPARAM wparam, LPARAM lparam)
{
	return Win32Application::OnMessage(msg, wparam, lparam);
}


void DijkstraApp::OnMouseDown(DWORD button, int x, int y)
{
	if (m_mode == 0 && m_vertices.size() > 0 &&  m_vertexIterator != m_vertices.end()) {
		m_vertexIterator->x = (float)x;
		m_vertexIterator->y = (float)y;
		m_graph.SetVertex(m_vertexIterator->i, *m_vertexIterator);
	}

	if (m_mode == 1 || m_mode == 2 && m_vertices.size() > 0) {
		for (auto it = m_vertices.begin(); it != m_vertices.end(); it++) {
			auto dx = it->x - x;
			auto dy = it->y - y;
			auto dist = sqrtf(dx * dx + dy * dy);
			if (dist < 20) {
				if (m_mode == 1) {
					m_source = *it;
					m_target = Vertex::Null;
					m_dijkstra.CalculateShortestDistances(&m_graph, m_source.i);
				}
				if (m_mode == 2) {
					m_target = *it;
				}
				break;
			}
		}
	}

	Refresh();
}


void DijkstraApp::OnMouseUp(DWORD button, int x, int y)
{
}


void DijkstraApp::OnMouseMove(DWORD button, int x, int y)
{
	m_mousePosition = PointF((float)x, (float)y);

	Refresh();
}


void DijkstraApp::OnKeyDown(DWORD key)
{
	if (key == VK_F5) {
		if (OpenFileDialog(L"Graph Files (*.graph)\0*.graph\0", GetAppDir(), m_graphFileName)) {
			GraphReader r(m_graph);
			r.Read(m_graphFileName);
			
			m_layoutFileName = ChangeFileExt(m_graphFileName, L".layout");
			if (FileExists(m_layoutFileName)) {
				LayoutReader lr(m_graph);
				lr.Read(m_layoutFileName);
			}

			m_vertices = m_graph.GetVertices();
			m_vertexIterator = m_vertices.begin();
			m_edges = m_graph.GetEdges();
			m_edgeIterator = m_edges.begin();
			m_source = Vertex::Null;
			m_target = Vertex::Null;
			m_mode = 0;
		}
	}

	if (key == VK_F6) {
		if (m_layoutFileName != L"") {
			LayoutWriter w(m_graph);
			w.Write(m_layoutFileName);
		}
	}

	if (key == VK_F1) {
		m_mode = 0;
	}

	if (key == VK_F2) {
		m_mode = 1;
	}

	if (key == VK_F3) {
		m_mode = 2;
	}

	if ((key & VK_TAB) == VK_TAB) {
		if (m_vertices.size() > 0) {
			m_vertexIterator++;

			if (m_vertexIterator == m_vertices.end()) {
				m_vertexIterator = m_vertices.begin();
			}

			Refresh();
		}
	}

	if ((key & VK_BACK) == VK_BACK) {
		if (m_edges.size() > 0) {
			m_edgeIterator++;

			if (m_edgeIterator == m_edges.end()) {
				m_edgeIterator = m_edges.begin();
			}

			Refresh();
		}
	}
}


void DijkstraApp::OnChar(char c)
{
}


void DijkstraApp::OnPaint(Graphics& graphics)
{
	graphics.SetSmoothingMode(SmoothingModeAntiAlias);
	graphics.Clear(Color::White);

	DrawAppInfo(graphics);
	DrawGraphInfo(graphics);
	DrawDijkstraInfo(graphics);
	DrawGraph(graphics);
	DrawDijkstra(graphics);
}


void DijkstraApp::DrawAppInfo(Graphics& graphics)
{
	wchar_t s[80];

	Font headerFont(L"Segoe UI", 12, FontStyleBold);
	Font textFont(L"Segoe UI", 12);
	SolidBrush textBrush(Color::Black);

	if (m_mode == 0)
		swprintf_s(s, L"%s", L"use TAB to select vertices and mouse click to place them");
	if (m_mode == 1)
		swprintf_s(s, L"%s", L"select SOURCE vertex with mouse click to calculate distances");
	if (m_mode == 2)
		swprintf_s(s, L"%s", L"select TARGET vertex with mouse click to show path");
	
	graphics.DrawString(
		s, wcslen(s),
		&textFont,
		PointF(10, 10),
		&textBrush);
}


void DijkstraApp::DrawGraphInfo(Graphics& graphics)
{
	wchar_t s[80];

	Font headerFont(L"Segoe UI", 12, FontStyleBold);
	Font textFont(L"Segoe UI", 12);
	SolidBrush textBrush(Color::Black);

	swprintf_s(s, L"Graph:");
	graphics.DrawString(
		s, wcslen(s),
		&headerFont,
		PointF(10, 40),
		&textBrush);

	swprintf_s(s, L"%d vertices, %d edges\n", m_graph.VertexCount(), m_graph.EdgeCount());
	graphics.DrawString(
		s, wcslen(s),
		&textFont,
		PointF(10, 55),
		&textBrush);


	if (m_vertices.size() > 0 && m_vertexIterator != m_vertices.end()) {
		swprintf_s(s, L"vertex: %d (%f, %f)\n", m_vertexIterator->i, m_vertexIterator->x, m_vertexIterator->y);
		graphics.DrawString(
			s, wcslen(s),
			&textFont,
			PointF(10, 70),
			&textBrush);
	}

	if (m_edges.size() > 0 && m_edgeIterator != m_edges.end()) {
		swprintf_s(s, L"edge: %d -> %d (%f)\n", m_edgeIterator->i, m_edgeIterator->j, m_edgeIterator->weight);
		graphics.DrawString(
			s, wcslen(s),
			&textFont,
			PointF(10, 85),
			&textBrush);
	}
}


void DijkstraApp::DrawGraph(Graphics& graphics)
{
	wchar_t s[80];

	Pen edgePen(Color::Red, 2.0f);
	Pen selectedEdgePen(Color::Orange, 4.0f);
	Font weightFont(L"Segoe UI", 10.0, FontStyleBold);
	SolidBrush weightBrush(Color::Red);
	
	auto edges = m_graph.GetEdges();
	for (auto it = edges.begin(); it != edges.end(); it++) {
		auto v1 = m_graph.GetVertex(it->i);
		auto v2 = m_graph.GetVertex(it->j);
		
		auto rx = v2.x - v1.x;
		auto ry = v2.y - v1.y;
		auto rl = sqrtf(rx * rx + ry * ry);
		auto dx = ry / rl * 10.0f;
		auto dy = -rx / rl * 10.0f;
		auto ox = rx / rl * 15.0f;
		auto oy = ry / rl * 15.0f;

		graphics.DrawLine(
			&edgePen,
			v1.x + dx + ox, v1.y + dy + oy, v2.x + dx - ox, v2.y + dy - oy);
		graphics.DrawLine(
			&edgePen,
			v2.x + dx - ox, v2.y + dy - oy,
			v2.x - 2.0f * ox + 1.5f * dx, v2.y - 2.0f * oy + 1.5f * dy);
		graphics.DrawLine(
			&edgePen,
			v2.x + dx - ox, v2.y + dy - oy,
			v2.x - 2.0f * ox + 0.5f * dx, v2.y - 2.0f * oy + 0.5f * dy);

		if (it->i == m_edgeIterator->i && it->j == m_edgeIterator->j) {
			graphics.DrawLine(
				&selectedEdgePen,
				v1.x + dx, v1.y + dy, v2.x + dx, v2.y + dy);
		}

		auto mx = v1.x + rx / 2;
		auto my = v1.y + ry / 2;

		swprintf_s(s, L"%.0f", it->weight);
		graphics.DrawString(
			s, wcslen(s),
			&weightFont,
			PointF(mx + dx, my + dy),
			&weightBrush);
	}

	Pen vertexPen(Color::DarkRed, 2.0f);
	SolidBrush vertexBrush(Color::Black);
	Pen selectedVertexPen(Color::Orange, 2.0f);
	Font textFont(L"Segoe UI", 12.0, FontStyleBold);
	SolidBrush textBrush(Color::White);

	auto vertexRadius = 15.0f;
	auto selectionRadius = 20.0f;
	auto textOffset = 1.0f;

	auto vertices = m_graph.GetVertices();
	for (auto it = vertices.begin(); it != vertices.end(); it++) {
		graphics.FillEllipse(&vertexBrush, it->x - vertexRadius, it->y - vertexRadius, vertexRadius * 2, vertexRadius * 2);

		if (it->i == m_vertexIterator->i) {
			graphics.DrawEllipse(&selectedVertexPen, it->x - selectionRadius, it->y - selectionRadius, selectionRadius * 2, selectionRadius * 2);
		}

		swprintf_s(s, L"%d", it->i);

		RectF bounds;
		graphics.MeasureString(s, wcslen(s), &textFont, PointF(0, 0), &bounds);

		graphics.DrawString(
			s, wcslen(s),
			&textFont,
			PointF(it->x - bounds.Width / 2 + textOffset, it->y - bounds.Height / 2 + textOffset),
			&textBrush);
	}
}


void DijkstraApp::DrawDijkstraInfo(Graphics& graphics)
{
	wchar_t s[80];

	Font headerFont(L"Segoe UI", 12, FontStyleBold);
	Font textFont(L"Segoe UI", 12);
	SolidBrush textBrush(Color::Black);

	swprintf_s(s, L"Dijkstra:");
	graphics.DrawString(
		s, wcslen(s),
		&headerFont,
		PointF(10, 115),
		&textBrush);

	if (m_source.IsNull())
		swprintf_s(s, L"source: n/a", m_source.i, m_source.x, m_source.y);
	else
		swprintf_s(s, L"source: %d (%.0f, %.0f)\n", m_source.i, m_source.x, m_source.y);
	graphics.DrawString(
		s, wcslen(s),
		&textFont,
		PointF(10, 130),
		&textBrush);

	if (m_target.IsNull())
		swprintf_s(s, L"target: n/a", m_source.i, m_source.x, m_source.y);
	else
		swprintf_s(s, L"target: %d (%.0f, %.0f)\n", m_target.i, m_target.x, m_target.y);
	graphics.DrawString(
		s, wcslen(s),
		&textFont,
		PointF(10, 145),
		&textBrush);
}


void DijkstraApp::DrawDijkstra(Graphics& graphics)
{
	if (m_source.IsNull()) return;
	if (m_dijkstra.Source() != m_source.i) return;

	wchar_t s[80];

	Font textFont(L"Segoe UI", 10);
	SolidBrush textBrush(Color::Black);

	auto distances = m_dijkstra.GetDistances();
	for (auto it = distances.begin(); it != distances.end(); it++) {
		auto vertex = m_graph.GetVertex(it->current);

		if (it->previous == -1)
			swprintf_s(s, L"source");
		else
			swprintf_s(s, L"%.0f \u2190 %d", it->distance, it->previous);
		graphics.DrawString(
			s, wcslen(s),
			&textFont,
			PointF(vertex.x + 10, vertex.y + 10),
			&textBrush);
	}

	if (m_target.IsNull()) return;

	Pen pathPen(Color::Lime, 4.0f);

	auto path = m_dijkstra.GetShortestPath(m_target.i);
	for (auto it = path.begin()++; it != path.end(); it++) {
		if (it->previous != -1) {
			auto v1 = m_graph.GetVertex(it->current);
			auto v2 = m_graph.GetVertex(it->previous);
			graphics.DrawLine(
				&pathPen,
				v1.x, v1.y, v2.x, v2.y);
		}
	}
}
